﻿using System;
using System.IO.Pipes;
using WeDonekRpc.IOBuffer;
using WeDonekRpc.IOBuffer.Interface;
using WeDonekRpc.PipeClient.Config;
using WeDonekRpc.PipeClient.Log;
using WeDonekRpc.PipeClient.Model;

namespace WeDonekRpc.PipeClient.Client
{
    /// <summary>
    /// 客户端
    /// </summary>
    internal class PipeClient
    {
        private NamedPipeClientStream _Stream;
        public string PipeName
        {
            get;
        }
        public string Host
        {
            get;
        }
        public PipeClient (string host, string pipeName)
        {
            this.Host = host;
            this.PipeName = pipeName;
        }
        private readonly Guid _ClientId = Guid.NewGuid();
        private ISocketBuffer _Buffer = null;
        private int _ver = 0;
        /// <summary>
        /// 客户端ID
        /// </summary>
        internal Guid ClientId => this._ClientId;

        private Interface.IClientEvent _Client;

        /// <summary>
        /// 客户端
        /// </summary>
        internal Interface.IClientEvent Client
        {
            set => this._Client = value;
        }
        /// <summary>
        /// 异步发送字节流
        /// </summary>
        /// <param name="buffer"></param>
        private bool Send (ISocketBuffer buffer)
        {
            if (!this._Stream.IsConnected)
            {
                return false;
            }
            try
            {
                _ = this._Stream.BeginWrite(buffer.Stream, 0, buffer.BufferSize, this._SendCallback, buffer);
                return true;
            }
            catch (Exception e)
            {
                IoLogSystem.AddSendLog(e, this);
                this.Close();
                buffer.Dispose();
                return false;
            }
        }
        internal bool Send (DataPage page)
        {
            ISocketBuffer buffer = PipeTools.GetSendBuffer(page);
            return this.Send(buffer);
        }
        public bool ConnectServer ()
        {
            try
            {
                this._Stream = new NamedPipeClientStream(this.Host, this.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
                this.InitSocket();
                this._Client.ConnectComplateEvent();
                return true;
            }
            catch (Exception e)
            {
                IoLogSystem.AddConnectLog(e, this);
                this.Close(); ;
                return false;
            }
        }

        private void _SendCallback (IAsyncResult ar)
        {
            ISocketBuffer buffer = (ISocketBuffer)ar.AsyncState;
            int ver = buffer.Ver;
            try
            {
                this._Stream.EndWrite(ar);
                this._Stream.Flush();
                this._Client.SendPageComplateEvent(buffer.PageId);
            }
            catch (Exception e)
            {
                this._Client.SendPageErrorEvent(buffer.PageId);
                IoLogSystem.AddSendLog(e, this);
                this.Close();
                return;
            }
            finally
            {
                buffer.Dispose(ver);
            }
        }

        private int _Size;
        /// <summary>
        /// 初始化Socket连接
        /// </summary>
        private void InitSocket ()
        {
            this._Size = PipeConfig.BufferSize;
            this._Buffer = BufferCollect.ApplyBuffer(this._Size, ref this._ver);
            this._Stream.Connect(PipeConfig.ConTimeout);
            this._Stream.ReadMode = PipeTransmissionMode.Byte;
            _ = this._Stream.BeginRead(this._Buffer.Stream, 0, this._Buffer.BufferSize, new AsyncCallback(this._Read), null);
        }

        private Model.DataPageInfo _CurrentPage;
        /// <summary>
        /// 异步回调
        /// </summary>
        /// <param name="evt"></param>
        private void _Read (IAsyncResult evt)
        {
            try
            {
                int size = this._Stream.EndRead(evt);
                if (size == 0)
                {
                    this.Close();
                    return;
                }
                if (!PipeTools.SplitPage(this._Buffer.Stream, ref size, ref this._CurrentPage, this))
                {
                    this.Close();
                    return;
                }
                this._Buffer.SetBufferSize(this._Size);
            }
            catch (Exception e)
            {
                IoLogSystem.AddReceiveLog(e, this);
                this.Close();
            }
            finally
            {
                try
                {
                    if (this._Stream.IsConnected)
                    {
                        _ = this._Stream.BeginRead(this._Buffer.Stream, 0, this._Buffer.BufferSize, new AsyncCallback(this._Read), null);
                    }
                }
                catch (Exception e)
                {
                    IoLogSystem.AddReceiveLog(e, this);
                    this.Close();
                }
            }
        }

        /// <summary>
        /// 接收数据完成时调用
        /// </summary>
        internal void ReceiveComplate ()
        {
            if (this._CurrentPage.LoadProgress == Enum.PageLoadProgress.加载完成)
            {
                this._Client.AllotEvent(this._CurrentPage);
            }
            this._CurrentPage = null;
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close ()
        {
            this._Client?.ConCloseEvent();
            this._Buffer?.Dispose(this._ver);
            if (this._Stream.IsConnected)
            {
                this._Stream.Close();
            }
            this._Stream.Dispose();
        }

    }
}
